﻿using System.Reflection.Emit;
using System.Reflection;
using System;
using ProxyViewModel.Commands;
using System.Collections.Generic;
namespace ProxyViewModel.Emitors {
	public class ContainedViewModelEmitor : EmitorBase {
		public ContainedViewModelEmitor(TypeBuilder typeBuilder, Type operatable, IDictionary<string, object> parameters)
			: base(typeBuilder, operatable, parameters) { }

		protected override void EmitContent() {
			TypeBuilder.AddInterfaceImplementation(typeof(IContainedViewModel));
			FieldBuilder field = DefineField();
			DefineSetMethod(field);
			MethodBuilder invalidateParentViewModel = DefineInvalidateParentCommandsMethod(field);
			CurrentParameters[Parameters.InvalidateParentViewModelMethodKey] = invalidateParentViewModel;
		}

		private FieldBuilder DefineField() {
			FieldBuilder builder = TypeBuilder.DefineField("_parentViewModel", typeof(object), FieldAttributes.Private);
			return builder;
		}

		private void DefineSetMethod(FieldBuilder parentViewModelField) {
			MethodBuilder method = TypeBuilder.DefineMethod("SetParentViewModel", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.Final,
				CallingConventions.Standard, null, new Type[] { typeof(object) });
			ILGenerator il = method.GetILGenerator();
			il.Emit(OpCodes.Ldarg_0);
			il.Emit(OpCodes.Ldarg_1);
			il.Emit(OpCodes.Stfld, parentViewModelField);
			il.Emit(OpCodes.Ret);
		}

		private MethodBuilder DefineInvalidateParentCommandsMethod(FieldBuilder parentViewModelField) {
			MethodBuilder method = TypeBuilder.DefineMethod("InvalidateParentViewModel", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.Final,
				CallingConventions.Standard, null, null);
			ILGenerator il = method.GetILGenerator();
			il.DeclareLocal(typeof(bool));
			il.Emit(OpCodes.Nop);
			il.Emit(OpCodes.Ldarg_0);
			il.Emit(OpCodes.Ldfld, parentViewModelField);
			il.Emit(OpCodes.Ldnull);
			il.Emit(OpCodes.Ceq);
			il.Emit(OpCodes.Stloc_0);
			il.Emit(OpCodes.Ldloc_0);
			Label label = il.DefineLabel();
			il.Emit(OpCodes.Brtrue_S, label);
			il.Emit(OpCodes.Nop);
			il.Emit(OpCodes.Ldarg_0);
			il.Emit(OpCodes.Ldfld, parentViewModelField);
			il.Emit(OpCodes.Call, typeof(ProxyCommandManager).GetMethod("InvalidateRequerySuggested", BindingFlags.Public | BindingFlags.Static));
			il.Emit(OpCodes.Nop);
			il.Emit(OpCodes.Nop);
			il.MarkLabel(label);
			il.Emit(OpCodes.Ret);
			return method;
		}
	}
}
